package cn.infinite.security.configuration;

import cn.infinite.security.core.SmsAuthenticateAble;
import cn.infinite.security.filter.SmsAuthenticationFilter;
import cn.infinite.security.handler.DefaultSmsAuthenticationFailureHandler;
import cn.infinite.security.handler.DefaultSmsAuthenticationSuccessHandlerImpl;
import cn.infinite.security.handler.SmsAuthenticationFailureHandler;
import cn.infinite.security.handler.SmsAuthenticationSuccessHandler;
import cn.infinite.security.point.SmsCheckCodeGenerateAble;
import cn.infinite.security.provider.SmsAuthenticationProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import javax.annotation.Resource;

@Slf4j
@Order(98)
@Configuration
@EnableConfigurationProperties({SmsConfigurerProperties.class, SecurityBasicProperties.class,
        ImageCheckCodeConfigProperties.class})
@ConditionalOnBean({SmsCheckCodeGenerateAble.class, SmsAuthenticateAble.class, RedisTemplate.class})
public class SmsAuthenticateAutoConfiguration extends WebSecurityConfigurerAdapter {

    public static final String CHILD_PATH_MATCHER = "/**";

    @Resource
    private SmsConfigurerProperties smsConfigurerProperties;

    @Resource
    private SmsAuthenticationSuccessHandler smsAuthenticationSuccessHandler;

    @Resource
    private SmsAuthenticationFailureHandler smsAuthenticationFailureHandler;

    @Resource
    private SmsAuthenticateAble smsAuthenticateAble;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 短信验证码认证策略
     *
     * @return
     */
    @Bean
    public AuthenticationProvider smsAuthenticationProvider() {
        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider(this.smsAuthenticateAble);
        return smsAuthenticationProvider;
    }

    @Bean
    @ConditionalOnMissingBean(SmsAuthenticationSuccessHandler.class)
    public SmsAuthenticationSuccessHandler smsAuthenticationSuccessHandler() {
        return new DefaultSmsAuthenticationSuccessHandlerImpl(this.smsConfigurerProperties);
    }

    @Bean
    @ConditionalOnMissingBean(SmsAuthenticationFailureHandler.class)
    public SmsAuthenticationFailureHandler smsAuthenticationFailureHandler() {
        DefaultSmsAuthenticationFailureHandler defaultSmsAuthenticationFailureHandler = new
                DefaultSmsAuthenticationFailureHandler();
        return defaultSmsAuthenticationFailureHandler;
    }

    private SmsAuthenticationFilter smsAuthenticationFilter() {
        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter(this.smsConfigurerProperties,
                this.redisTemplate);
        smsAuthenticationFilter.setAuthenticationSuccessHandler(this.smsAuthenticationSuccessHandler);
        smsAuthenticationFilter.setAuthenticationFailureHandler(this.smsAuthenticationFailureHandler);

        return smsAuthenticationFilter;
    }

    /**
     * 短信认证 核心配置 将认证相关对象 关联到
     * Spring Security filter chain 上
     *
     * @return
     */
    private SmsAuthenticationConfigurerAdapter smsAuthenticationConfigurer() {
        SmsAuthenticationConfigurerAdapter smsAuthenticationConfigurer = new SmsAuthenticationConfigurerAdapter(this
                .smsAuthenticationFilter(), this.smsAuthenticationProvider());
        return smsAuthenticationConfigurer;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.info("加载短信认证配置");

        //应用短信配置到安全框架上
        http.apply(this.smsAuthenticationConfigurer());
        http.antMatcher(this.smsConfigurerProperties.getScopePath().concat(CHILD_PATH_MATCHER)).authorizeRequests()
                .antMatchers(
                        this.smsConfigurerProperties.getSendSmsCodeEndPoint(), //下发短信验证码端点无需认证
                        this.smsConfigurerProperties.getValidateSmsCodeEndPoint() //验证短信验证码无需认证
                ).permitAll().anyRequest().authenticated().and().csrf().disable();
    }
}
